package com.drg.center.dao;

import cn.hutool.core.util.ArrayUtil;
import com.alibaba.fastjson.JSONObject;
import com.drg.center.entity.PatientCaseEsPO;
import com.drg.center.entity.param.CaseMonitorPageDTO;
import com.drg.center.entity.param.CaseProfitLossDTO;
import com.drg.center.entity.result.*;
import com.drg.center.mapper.PatientCaseEsMapper;
import com.drg.center.utils.EsResultResolveUtil;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.guaike.core.utils.BigDecimalUtil;
import com.guaike.core.utils.CompareUtil;
import com.guaike.core.utils.LocalDateUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.script.Script;
import org.elasticsearch.script.ScriptType;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.aggregations.*;
import org.elasticsearch.search.aggregations.bucket.filter.FilterAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.filter.ParsedFilter;
import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramAggregationBuilder;
import org.elasticsearch.search.aggregations.bucket.histogram.DateHistogramInterval;
import org.elasticsearch.search.aggregations.bucket.histogram.Histogram;
import org.elasticsearch.search.aggregations.bucket.histogram.ParsedDateHistogram;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Repository;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;

import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
import static org.elasticsearch.index.query.QueryBuilders.existsQuery;

/**
 * Es 病历维度聚合统计
 * @Author: zhangxilin
 * @Date: 2024/12/5
 */
@Slf4j
@Repository
public class EsCaseAngleDAO {

    @Autowired
    private PatientCaseEsMapper patientCaseEsMapper;

    @Autowired
    private ElasticsearchOperations elasticsearchOperations;


    /**
     *  总览
     *  DSL-002
     * @param departCode
     * @param startDate
     * @param endDate
     * @return
     */
    public PatientCaseSttVO overveiw(String departCode, LocalDate startDate, LocalDate endDate){
        // 检索条件
        BoolQueryBuilder queryBuilder = boolQuery();
        queryBuilder.must(QueryBuilders.termQuery("hasOut", true));
        if(StringUtils.isNotBlank(departCode)){
            queryBuilder.must(QueryBuilders.termQuery("outDepartCode", departCode));
        }
        if(startDate != null){
            queryBuilder.must(QueryBuilders.rangeQuery("outHospitalDate").gte(LocalDateUtil.getTimestamp(startDate)));
        }
        if(endDate != null){
            queryBuilder.must(QueryBuilders.rangeQuery("outHospitalDate").lt(LocalDateUtil.getTimestamp(endDate.plusDays(1))));
        }

        NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(queryBuilder)
                .withPageable(PageRequest.of(0, 1))
                .addAggregation(AggregationBuilders.count("caseCount").field("caseCode"))
                .addAggregation(AggregationBuilders.filter("caseInDrgCount",
                        boolQuery().must(existsQuery("drgCode"))
                                   .mustNot(QueryBuilders.termQuery("drgCode", "0000"))
                ))
                .addAggregation(AggregationBuilders.cardinality("drgCount").field("drgCode"))
                .addAggregation(AggregationBuilders.sum("expectCost").field("expectCost").format("0.00"))
                .addAggregation(AggregationBuilders.sum("totalCost").field("totalCost").format("0.00"))
                .addAggregation(AggregationBuilders.sum("medicine_cost").field("medicineCost").format("0.00"))
                .addAggregation(AggregationBuilders.sum("consumables_cost").field("consumablesCost").format("0.00"))
                .addAggregation(AggregationBuilders.sum("other_cost").field("otherCost").format("0.00"))

                .addAggregation(AggregationBuilders.avg("avgCost").field("totalCost").format("0.00"))
                .addAggregation(AggregationBuilders.avg("avg_medicine_cost").field("medicineCost").format("0.00"))
                .addAggregation(AggregationBuilders.avg("avg_consumables_cost").field("consumablesCost").format("0.00"))
                .addAggregation(AggregationBuilders.avg("avg_other_cost").field("otherCost").format("0.00"))
                .build();

        Aggregations aggregations =  elasticsearchOperations.search(searchQuery, PatientCaseEsPO.class).getAggregations();
        log.info("【EsCaseAngleDAO.overveiw】 aggregations: {}",JSONObject.toJSONString(aggregations));

        ValueCount caseCount = aggregations.get("caseCount");
        Cardinality drgCount = aggregations.get("drgCount");
        Sum totalCostSum = aggregations.get("totalCost");
        Sum expectCostSum = aggregations.get("expectCost");
        ParsedFilter caseInDrgCount = aggregations.get("caseInDrgCount");
        PatientCaseSttVO vo = new PatientCaseSttVO();
        vo.setCaseCount(Integer.parseInt(caseCount.getValue()+""));
        vo.setDrgCount(Integer.parseInt(drgCount.getValue()+""));
        vo.setCaseInDrgCount(Integer.parseInt(caseInDrgCount.getDocCount()+""));
        vo.setTotalCost(BigDecimalUtil.parseBigDecimal(totalCostSum.getValueAsString()));
        vo.setExpectCost(BigDecimalUtil.parseBigDecimal(expectCostSum.getValueAsString()));
        vo.setMedicineCost(EsResultResolveUtil.getSumValue(aggregations, "medicine_cost"));
        vo.setConsumablesCost(EsResultResolveUtil.getSumValue(aggregations, "consumables_cost"));
        vo.setOtherCost(EsResultResolveUtil.getSumValue(aggregations, "other_cost"));

        vo.setAvgCost(EsResultResolveUtil.getAvg(aggregations, "avgCost"));
        vo.setMedicineAvgCost(EsResultResolveUtil.getAvg(aggregations, "avg_medicine_cost"));
        vo.setConsumablesAvgCost(EsResultResolveUtil.getAvg(aggregations, "avg_consumables_cost"));
        vo.setOtherAvgCost(EsResultResolveUtil.getAvg(aggregations, "avg_other_cost"));


        return vo;
    }

    /**
     *  DRG费用汇总
     *  DSL-001
     * @param departCode
     * @param startDate
     * @param endDate
     * @return
     */
    public DrgSttVO drgstt(String departCode, LocalDate startDate, LocalDate endDate){
        // 检索条件
        BoolQueryBuilder queryBuilder = boolQuery();
        queryBuilder.must(QueryBuilders.termQuery("hasOut", true));
        queryBuilder.must(QueryBuilders.existsQuery("drgCode"));
        if(StringUtils.isNotBlank(departCode)){
            queryBuilder.must(QueryBuilders.termQuery("outDepartCode", departCode));
        }
        if(startDate != null){
            queryBuilder.must(QueryBuilders.rangeQuery("outHospitalDate").gte(LocalDateUtil.getTimestamp(startDate)));
        }
        if(endDate != null){
            queryBuilder.must(QueryBuilders.rangeQuery("outHospitalDate").lt(LocalDateUtil.getTimestamp(endDate.plusDays(1))));
        }

        NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(queryBuilder)
                .withPageable(PageRequest.of(0, 1))
                .addAggregation(AggregationBuilders.count("caseCount").field("caseCode"))
                .addAggregation(AggregationBuilders.sum("totalCost").field("totalCost").format("0.00"))
                .addAggregation(AggregationBuilders.cardinality("drgCount").field("drgCode"))
                .build();

        Aggregations aggregations =  elasticsearchOperations.search(searchQuery, PatientCaseEsPO.class).getAggregations();


        ValueCount caseCount = aggregations.get("caseCount");
        Cardinality drgCount = aggregations.get("drgCount");
        Sum totalCostSum = aggregations.get("totalCost");

        DrgSttVO vo = new DrgSttVO();
        vo.setCaseCount(Integer.parseInt(caseCount.getValue()+""));
        vo.setDrgCount(Integer.parseInt(drgCount.getValue()+""));
        vo.setTotalCost(BigDecimalUtil.parseBigDecimal(totalCostSum.getValueAsString()));

        log.info("【EsCaseAngleDAO.drgstt】 vo: {}",JSONObject.toJSONString(vo));
        return vo;
    }



    /**
     *  亏损科室top5
     *  DSL-003
     * @param startDate
     * @param endDate
     * @return
     */
    public List<KeyValueVO> getDepartTop5(LocalDate startDate, LocalDate endDate){
        // 检索条件
        BoolQueryBuilder queryBuilder = boolQuery();
        queryBuilder.must(QueryBuilders.termQuery("hasOut", true));
        if(startDate != null){
            queryBuilder.must(QueryBuilders.rangeQuery("outHospitalDate").gte(LocalDateUtil.getTimestamp(startDate)));
        }
        if(endDate != null){
            queryBuilder.must(QueryBuilders.rangeQuery("outHospitalDate").lt(LocalDateUtil.getTimestamp(endDate.plusDays(1))));
        }

        // 构建聚合
        TermsAggregationBuilder departmentsAgg = AggregationBuilders.terms("departments")
                .field("outDepartCode")
                .size(5)
                .order(BucketOrder.aggregation("total_profit", true));

        SumAggregationBuilder totalProfitAgg = AggregationBuilders.sum("total_profit").field("profit").format("0.00");

        TopHitsAggregationBuilder departNameAgg = AggregationBuilders.topHits("departName").size(1)
                .fetchSource(new String[]{"outDepartName"}, null);

        Map profitMap = Maps.newHashMap();
        profitMap.put("profitSum", "total_profit");

        NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(queryBuilder)
                .withPageable(PageRequest.of(0, 1))
                .addAggregation(departmentsAgg.subAggregation(totalProfitAgg).subAggregation(departNameAgg)
                    .subAggregation(PipelineAggregatorBuilders.bucketSelector("filter_negative_profit",
                            profitMap,
                            new Script(ScriptType.INLINE, "painless", "params.profitSum < 0", Collections.emptyMap())))
                )
                .build();

        Aggregations aggregations =  elasticsearchOperations.search(searchQuery, PatientCaseEsPO.class).getAggregations();
        Terms departments = aggregations.get("departments");

        List<KeyValueVO> list = Lists.newArrayList();
        for (Terms.Bucket entry : departments.getBuckets()) {
            String departmentCode = entry.getKeyAsString();
            ParsedSum sum = entry.getAggregations().get("total_profit");
            BigDecimal totalProfit = BigDecimalUtil.parseBigDecimal(sum.getValueAsString());
            totalProfit = totalProfit.abs();

            TopHits topHits = entry.getAggregations().get("departName");
            SearchHit[] hits = topHits.getHits().getHits();

            String departmentName = null;
            if (!ArrayUtil.isEmpty(hits)) {
                Map<String, Object> sourceAsMap = hits[0].getSourceAsMap();
                departmentName = (String) sourceAsMap.get("outDepartName");
            }
            list.add(new KeyValueVO(departmentCode,totalProfit, departmentName) );
        }
        log.info("【EsCaseAngleDAO.getTop5】 list: {}",JSONObject.toJSONString(list));

        return list;
    }


    /**
     *  DRG亏损TOP5
     *  DSL-004
     * @param departCode
     * @param startDate
     * @param endDate
     * @return
     */
    public List<KeyValueVO> getDrgTop5(String departCode, LocalDate startDate, LocalDate endDate){
        // 检索条件
        BoolQueryBuilder queryBuilder = boolQuery();
        queryBuilder.must(QueryBuilders.termQuery("hasOut", true));
        if(StringUtils.isNotBlank(departCode)){
            queryBuilder.must(QueryBuilders.termQuery("outDepartCode", departCode));
        }
        if(startDate != null){
            queryBuilder.must(QueryBuilders.rangeQuery("outHospitalDate").gte(LocalDateUtil.getTimestamp(startDate)));
        }
        if(endDate != null){
            queryBuilder.must(QueryBuilders.rangeQuery("outHospitalDate").lt(LocalDateUtil.getTimestamp(endDate.plusDays(1))));
        }

        // 构建聚合
        TermsAggregationBuilder departmentsAgg = AggregationBuilders.terms("departments")
                .field("drgCode")
                .size(5)
                .order(BucketOrder.aggregation("total_profit", true));

        SumAggregationBuilder totalProfitAgg = AggregationBuilders.sum("total_profit").field("profit").format("0.00");

        TopHitsAggregationBuilder departNameAgg = AggregationBuilders.topHits("drgName").size(1)
                .fetchSource(new String[]{"drgName"}, null);

        Map profitMap = Maps.newHashMap();
        profitMap.put("profitSum", "total_profit");

        NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(queryBuilder)
                .withPageable(PageRequest.of(0, 1))
                .addAggregation(departmentsAgg.subAggregation(totalProfitAgg).subAggregation(departNameAgg)
                        .subAggregation(PipelineAggregatorBuilders.bucketSelector("filter_negative_profit",
                                profitMap,
                                new Script(ScriptType.INLINE, "painless", "params.profitSum < 0", Collections.emptyMap())))
                )
                .build();

        Aggregations aggregations =  elasticsearchOperations.search(searchQuery, PatientCaseEsPO.class).getAggregations();
        Terms departments = aggregations.get("departments");

        List<KeyValueVO> list = Lists.newArrayList();
        for (Terms.Bucket entry : departments.getBuckets()) {
            String departmentCode = entry.getKeyAsString();
            ParsedSum sum = entry.getAggregations().get("total_profit");
            BigDecimal totalProfit = BigDecimalUtil.parseBigDecimal(sum.getValueAsString());
            totalProfit = totalProfit.abs();

            TopHits topHits = entry.getAggregations().get("drgName");
            SearchHit[] hits = topHits.getHits().getHits();

            String departmentName = null;
            if (!ArrayUtil.isEmpty(hits)) {
                Map<String, Object> sourceAsMap = hits[0].getSourceAsMap();
                departmentName = (String) sourceAsMap.get("drgName");
            }
            list.add(new KeyValueVO(departmentCode,totalProfit, departmentName) );
        }
        log.info("【EsCaseAngleDAO.getTop5】 list: {}",JSONObject.toJSONString(list));

        return list;
    }

    /**
     *  全院费用总趋势
     *  DSL-005
     * @param year
     * @return
     */
    public List<KeyValueVO> getCostTrend(Integer year){
        // 检索条件
        BoolQueryBuilder queryBuilder = boolQuery();
        queryBuilder.must(QueryBuilders.termQuery("hasOut", true));
        if(year != null){
            LocalDateTime firstDayOfYear = LocalDateUtil.getFirstDayOfYear(year);
            LocalDateTime lastDayOfYear = LocalDateUtil.getLastDayOfYear(year);
            queryBuilder.must(QueryBuilders.rangeQuery("outHospitalDate").gte(LocalDateUtil.getTimestamp(firstDayOfYear)));
            queryBuilder.must(QueryBuilders.rangeQuery("outHospitalDate").lte(LocalDateUtil.getTimestamp(lastDayOfYear)));
        }


        DateHistogramAggregationBuilder dateHistogramAgg = AggregationBuilders.dateHistogram("monthly_profit")
                .field("outHospitalDate")
                .calendarInterval(DateHistogramInterval.MONTH)
                .format("yyyy-MM")
                .timeZone(ZoneId.of("+08:00"));
        SumAggregationBuilder expectProfitSum = AggregationBuilders.sum("expectProfit").field("expectProfit").format("0.00");
        SumAggregationBuilder profitSum = AggregationBuilders.sum("profit").field("profit").format("0.00");

        NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(queryBuilder)
                .addAggregation(dateHistogramAgg.subAggregation(expectProfitSum).subAggregation(profitSum))
                .withPageable(PageRequest.of(0, 1))
                .build();

        Aggregations aggregations =  elasticsearchOperations.search(searchQuery, PatientCaseEsPO.class).getAggregations();

        Aggregation aggregation  = aggregations.get("monthly_profit");

        List<KeyValueVO> list = Lists.newArrayList();
        if (aggregation instanceof ParsedDateHistogram) {
            ParsedDateHistogram dateHistogram = (ParsedDateHistogram) aggregation;
            dateHistogram.getBuckets().forEach(bucket ->{
                String keyAsString = bucket.getKeyAsString();
                String expectProfitSumValue = ((Sum) bucket.getAggregations().get("expectProfit")).getValueAsString();
                String profitSumValue = ((Sum) bucket.getAggregations().get("profit")).getValueAsString();
                KeyValueVO vo =new KeyValueVO(keyAsString, new BigDecimal(profitSumValue), new BigDecimal(expectProfitSumValue));
                list.add(vo);
            });
        }
        log.info("【EsCaseAngleDAO.getCostTrend】 list: {}",JSONObject.toJSONString(list));
        return list;
    }

    /**
     * 入组率趋势统计
     * DSL-006
     * @param year
     * @return
     */
    public List<KeyValueVO> getInDrgTrend(Integer year){
        // 检索条件
        BoolQueryBuilder queryBuilder = boolQuery();
        queryBuilder.must(QueryBuilders.termQuery("hasOut", true));
        if(year != null){
            LocalDateTime firstDayOfYear = LocalDateUtil.getFirstDayOfYear(year);
            LocalDateTime lastDayOfYear = LocalDateUtil.getLastDayOfYear(year);
            queryBuilder.must(QueryBuilders.rangeQuery("outHospitalDate").gte(LocalDateUtil.getTimestamp(firstDayOfYear)));
            queryBuilder.must(QueryBuilders.rangeQuery("outHospitalDate").lte(LocalDateUtil.getTimestamp(lastDayOfYear)));
        }

        DateHistogramAggregationBuilder dateHistogramAgg = AggregationBuilders.dateHistogram("per_month")
                .field("outHospitalDate")
                .calendarInterval(DateHistogramInterval.MONTH)
                .format("yyyy-MM")
                .timeZone(ZoneId.of("+08:00"));

        ValueCountAggregationBuilder totalAggs = AggregationBuilders.count("total_cases").field("caseCode");
        FilterAggregationBuilder drgAggs = AggregationBuilders.filter("cases_with_drg",
                boolQuery().must(existsQuery("drgCode"))
                           .mustNot(QueryBuilders.termQuery("drgCode", "0000"))
        )
         .subAggregation(AggregationBuilders.count("count_with_drg").field("caseCode"));

        NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(queryBuilder)
                .addAggregation(dateHistogramAgg.subAggregation(totalAggs).subAggregation(drgAggs))
                .withPageable(PageRequest.of(0, 1))
                .build();

        Aggregations aggregations =  elasticsearchOperations.search(searchQuery, PatientCaseEsPO.class).getAggregations();

        Histogram perMonthAgg = aggregations.get("per_month");
        List<? extends Histogram.Bucket> buckets = perMonthAgg.getBuckets();

        List<KeyValueVO> list = Lists.newArrayList();
        for (Histogram.Bucket bucket : buckets) {
            String month = bucket.getKeyAsString();
            long totalCases = ((ParsedValueCount) bucket.getAggregations().get("total_cases")).getValue();
            long casesWithDrg = ((ParsedValueCount) ((ParsedFilter) bucket.getAggregations().get("cases_with_drg")).getAggregations().get("count_with_drg")).getValue();
            list.add(new KeyValueVO(month, BigDecimalUtil.calculatePercentage(casesWithDrg, totalCases)));
        }
        log.info("【EsCaseAngleDAO.getInDrgTrend】 list: {}",JSONObject.toJSONString(list));
        return list;
    }

    /**
     * 科室盈亏占比
     * DSL-007
     * @param startDate
     * @param endDate
     * @return
     */
    public ProfitLossMixVO getDepartProfitRateStt(LocalDate startDate, LocalDate endDate){
        // 检索条件
        BoolQueryBuilder queryBuilder = boolQuery();
        queryBuilder.must(QueryBuilders.termQuery("hasOut", true));
        if(startDate != null){
            queryBuilder.must(QueryBuilders.rangeQuery("outHospitalDate").gte(LocalDateUtil.getTimestamp(startDate)));
        }
        if(endDate != null){
            queryBuilder.must(QueryBuilders.rangeQuery("outHospitalDate").lt(LocalDateUtil.getTimestamp(endDate.plusDays(1))));
        }

        // 构建聚合
        TermsAggregationBuilder departmentsAgg = AggregationBuilders.terms("profit_aggs")
                .field("outDepartCode")
                .size(1000);

        SumAggregationBuilder totalProfitAgg = AggregationBuilders.sum("total_profit")
                .field("profit").format("0.00");

        departmentsAgg.subAggregation(totalProfitAgg);

        NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(queryBuilder)
                .addAggregation(departmentsAgg)
                .withPageable(PageRequest.of(0, 1))
                .build();

        Aggregations aggregations =  elasticsearchOperations.search(searchQuery, PatientCaseEsPO.class).getAggregations();

        // 解析部门聚合
        Terms terms = aggregations.get("profit_aggs");
        int total = 0;
        int profitCount = 0;
        int lossCount = 0;
        for (Terms.Bucket entry: terms.getBuckets()){
            // 解析利润总和
            ParsedSum sumProfit = entry.getAggregations().get("total_profit");
            double totalProfit = sumProfit.getValue();
            ++total;
            if(totalProfit >=0 ){
                ++ profitCount;
            }else{
                ++lossCount;
            }
        }

        BigDecimal rate = BigDecimalUtil.calculatePercentage(profitCount, total);

        ProfitLossMixVO vo = new ProfitLossMixVO();
        vo.setAllCount(total);
        vo.setProfitCount(profitCount);
        vo.setLossCount(lossCount);
        vo.setProfitRatio(rate+"");

        log.info("【EsCaseAngleDAO.getDepartProfitRateStt】 vo: {}",JSONObject.toJSONString(vo));
        return vo;
    }

    /**
     * 病组盈亏占比
     * DSL-008
     * @param departCode
     * @param startDate
     * @param endDate
     * @return
     */
    public ProfitLossMixVO getDrgProfitRateStt(String departCode,  LocalDate startDate, LocalDate endDate){
        // 检索条件
        BoolQueryBuilder queryBuilder = boolQuery();
        queryBuilder.must(QueryBuilders.termQuery("hasOut", true));
        if(StringUtils.isNotBlank(departCode)){
            queryBuilder.must(QueryBuilders.termQuery("outDepartCode", departCode));
        }
        if(startDate != null){
            queryBuilder.must(QueryBuilders.rangeQuery("outHospitalDate").gte(LocalDateUtil.getTimestamp(startDate)));
        }
        if(endDate != null){
            queryBuilder.must(QueryBuilders.rangeQuery("outHospitalDate").lt(LocalDateUtil.getTimestamp(endDate.plusDays(1))));
        }

        // 构建聚合
        TermsAggregationBuilder departmentsAgg = AggregationBuilders.terms("profit_aggs")
                .field("drgCode")
                .size(1000);

        SumAggregationBuilder totalProfitAgg = AggregationBuilders.sum("total_profit")
                .field("profit").format("0.00");

        departmentsAgg.subAggregation(totalProfitAgg);

        NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(queryBuilder)
                .addAggregation(departmentsAgg)
                .withPageable(PageRequest.of(0, 1))
                .build();

        Aggregations aggregations =  elasticsearchOperations.search(searchQuery, PatientCaseEsPO.class).getAggregations();

        // 解析部门聚合
        Terms terms = aggregations.get("profit_aggs");
        int total = 0;
        int profitCount = 0;
        int lossCount = 0;
        for (Terms.Bucket entry: terms.getBuckets()){
            // 解析利润总和
            ParsedSum sumProfit = entry.getAggregations().get("total_profit");
            double totalProfit = sumProfit.getValue();
            ++total;
            if(totalProfit >=0 ){
                ++ profitCount;
            }else{
                ++lossCount;
            }
        }

        BigDecimal rate = BigDecimalUtil.calculatePercentage(profitCount, total);

        ProfitLossMixVO vo = new ProfitLossMixVO();
        vo.setAllCount(total);
        vo.setProfitCount(profitCount);
        vo.setLossCount(lossCount);
        vo.setProfitRatio(rate+"");

        log.info("【EsCaseAngleDAO.getDrgProfitRateStt】 vo: {}",JSONObject.toJSONString(vo));
        return vo;
    }

    /**
     * 病历盈亏分析
     * DSL-009
     * @param param
     * @return
     */
    public CaseProfitLossAnalysisVO caseProfitLossAnalysis(CaseProfitLossDTO param){
        // 检索条件
        BoolQueryBuilder queryBuilder = boolQuery();
        queryBuilder.must(QueryBuilders.termQuery("hasOut", true));


        if(StringUtils.isNotBlank(param.getDepartCode())){
            queryBuilder.must(QueryBuilders.termQuery("departCode", param.getDepartCode()));
        }
        if(StringUtils.isNotBlank(param.getChsDrgCode())){
            queryBuilder.must(QueryBuilders.termQuery("drgCode", param.getChsDrgCode()));
        }
        if(param.getOutType() != null){
            queryBuilder.must(QueryBuilders.termQuery("outType", param.getOutType()));
        }
        if(param.getDieType() != null){
            // TODO: 2024/12/7 待产品确定
        }
        if(param.getStartDate() != null){
            queryBuilder.must(QueryBuilders.rangeQuery("inHospitalDate").gte(LocalDateUtil.getTimestamp(param.getStartDate())));
        }
        if(param.getEndDate() != null){
            queryBuilder.must(QueryBuilders.rangeQuery("inHospitalDate").lt(LocalDateUtil.getTimestamp(param.getEndDate().plusDays(1))));
        }
        if(StringUtils.isNotBlank(param.getSearch())){
            BoolQueryBuilder shouldQuery = QueryBuilders.boolQuery();
            shouldQuery.should(QueryBuilders.termQuery("caseCode", param.getSearch()));
            shouldQuery.should(QueryBuilders.matchPhraseQuery("doctorName", param.getSearch()));
            shouldQuery.should(QueryBuilders.nestedQuery("personInfoVO",
                    QueryBuilders.matchPhraseQuery("personInfoVO.patientName", param.getSearch()),
                    ScoreMode.None));
            shouldQuery.minimumShouldMatch("1");
            queryBuilder.must(shouldQuery);
        }

        // 构建聚合
        NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(queryBuilder)
                .addAggregation(AggregationBuilders.count("caseCount").field("caseCode"))
                .addAggregation(AggregationBuilders.sum("totalCost").field("totalCost").format("0.00"))
                .addAggregation(AggregationBuilders.sum("expectCost").field("expectCost").format("0.00"))
                .addAggregation(AggregationBuilders.sum("profit").field("profit").format("0.00"))
                .addAggregation(AggregationBuilders.sum("totalPoints").field("basisPoints").format("0.00"))
//                .addAggregation(AggregationBuilders.filter("caseInDrgCount",
//                        QueryBuilders.boolQuery().must(QueryBuilders.existsQuery("drgCode")))
//                        .subAggregation(AggregationBuilders.count("count").field("caseCode")))
//                .addAggregation(AggregationBuilders.cardinality("drgCount").field("drgCode"))
                .addAggregation(AggregationBuilders.terms("magnification_counts")
                        .field("magnification")
//                        .includeExclude(new IncludeExclude("1|2", null))
                        .size(2))
                .withPageable(PageRequest.of(0, 1))
                .build();

        Aggregations aggregations =  elasticsearchOperations.search(searchQuery, PatientCaseEsPO.class).getAggregations();


//        // 解析聚合结果
        CaseProfitLossAnalysisVO result = new CaseProfitLossAnalysisVO();
        result.setTotalCase(((ValueCount) aggregations.get("caseCount")).getValue()+"");
        result.setTotalTreatCost(((Sum) aggregations.get("totalCost")).getValueAsString());
        result.setTotalSettleCost(((Sum) aggregations.get("expectCost")).getValueAsString());
        result.setNetProfitLoss(((Sum) aggregations.get("profit")).getValueAsString());
        result.setTotalPoints(((Sum) aggregations.get("totalPoints")).getValueAsString());


        Terms magnificationCounts = (Terms) aggregations.get("magnification_counts");
        for (Terms.Bucket bucket : magnificationCounts.getBuckets()) {
            if(CompareUtil.isEqual(bucket.getKeyAsString(), "1")){
                result.setLowCase(bucket.getDocCount()+"");
            }
            if(CompareUtil.isEqual(bucket.getKeyAsString(), "2")){
                result.setHighCase(bucket.getDocCount()+"");
            }
        }
        log.info("【EsCaseAngleDAO.aggregations】 result: {}",JSONObject.toJSONString(result));
        return result;

    }

    /**
     *  在院病历统计
     *  DSL-021
     * @param param
     * @return
     */
    public CaseMonitorStatVO getCaseMonitorStat(CaseMonitorPageDTO param){
        // 检索条件
        BoolQueryBuilder queryBuilder = boolQuery();
        queryBuilder.must(QueryBuilders.termQuery("hasOut", false));
        if(StringUtils.isNotBlank(param.getDepartCode())){
            queryBuilder.must(QueryBuilders.termQuery("departCode", param.getDepartCode()));
        }
        if(StringUtils.isNotBlank(param.getChsDrgCode())){
            queryBuilder.must(QueryBuilders.termQuery("drgCode", param.getChsDrgCode()));
        }
        if(StringUtils.isNotBlank(param.getDoctorName())){
            queryBuilder.must(QueryBuilders.termQuery("doctorName.keyword", param.getDoctorName()));
        }
        if(StringUtils.isNotBlank(param.getPatientName())){
            queryBuilder.must(QueryBuilders.nestedQuery("personInfoVO", QueryBuilders.termQuery("personInfoVO.patientName.keyword", param.getPatientName()), ScoreMode.None));
        }

        if(param.getVisitDate() != null){
            queryBuilder.must(QueryBuilders.rangeQuery("inHospitalDate").gte(LocalDateUtil.getTimestamp(param.getVisitDate())));
            queryBuilder.must(QueryBuilders.rangeQuery("inHospitalDate").lt(LocalDateUtil.getTimestamp(param.getVisitDate().plusDays(1))));
        }
        if(StringUtils.isNotBlank(param.getInpatientCode())){
            queryBuilder.must(QueryBuilders.termQuery("inpatientCode", param.getInpatientCode()));
        }

        NativeSearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(queryBuilder)
                .withPageable(PageRequest.of(0, 1))
                .addAggregation(AggregationBuilders.count("caseCount").field("caseCode"))
                .addAggregation(AggregationBuilders.filter("overbudget-count",
                        QueryBuilders.rangeQuery("overExpectCost").gt(0))
                        .subAggregation(AggregationBuilders.count("caseCount").field("caseCode"))
                )
                .addAggregation(AggregationBuilders.terms("magnification_counts")
                        .field("magnification")
                        .size(2))
                .addAggregation(AggregationBuilders.filter("problem_cases", QueryBuilders.termQuery("isNormalCase", false))
                        .subAggregation(AggregationBuilders.count("caseCount").field("caseCode"))
                )
                .build();

        Aggregations aggregations =  elasticsearchOperations.search(searchQuery, PatientCaseEsPO.class).getAggregations();



        CaseMonitorStatVO vo = new CaseMonitorStatVO();

        ValueCount caseCount = aggregations.get("caseCount");
        vo.setTotalCase((int)caseCount.getValue());

        // 解析 overbudget-count 聚合
        ParsedFilter filterOverBudget = aggregations.get("overbudget-count");
        ValueCount countOverBudget = filterOverBudget.getAggregations().get("caseCount");
        vo.setOverspendCase((int)countOverBudget.getValue());

        // 解析 magnification_counts 聚合
        vo.setLowCase(0);
        vo.setHighCase(0);
        Terms magnificationCounts = (Terms) aggregations.get("magnification_counts");
        for (Terms.Bucket bucket : magnificationCounts.getBuckets()) {
            if(CompareUtil.isEqual(bucket.getKeyAsString(), "1")){
                vo.setLowCase((int)bucket.getDocCount());
            }
            if(CompareUtil.isEqual(bucket.getKeyAsString(), "2")){
                vo.setHighCase((int)bucket.getDocCount());
            }
        }


        // 解析 problem_cases 聚合
        ParsedFilter filterProblemCases = aggregations.get("problem_cases");
        ValueCount filterProblemCasesCount = filterProblemCases.getAggregations().get("caseCount");
        vo.setProblemCase((int)filterProblemCasesCount.getValue());
        log.info("【EsCaseAngleDAO.getCaseMonitorStat】 vo: {}",JSONObject.toJSONString(vo));
        return vo;

    }

}
